package pile.pay.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.log4j.Log4j2;
import org.apache.shardingsphere.api.hint.HintManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pile.pay.config.alipayConfig.AlipaySdk;
import pile.pay.config.alipayConfig.AlipayTradeEntity;
import pile.pay.entity.BalanceChange;
import pile.pay.entity.Recharge;
import pile.pay.entity.RefundOrder;
import pile.pay.entity.ResponseResult;
import pile.pay.service.*;
import pile.pay.utils.EmailUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Log4j2
public class AliPayServiceImpl implements IAliPayService {

    @Autowired
    private AlipaySdk alipaySdk;

    @Autowired
    private IRechargeService rechargeService;

    @Value("${alipay.notifyUrl}")
    private  String notifyUrl;

    @Autowired
    private IRefundOrderService refundOrderService;

    @Autowired
    private IBalanceChangeService balanceChangeService;

    @Autowired
    private EmailUtil emailUtil;



    /**
     * 统一下单
     *
     * @param alipayTradeEntity
     * @return
     */
    @Override
    public ResponseResult transactions(AlipayTradeEntity alipayTradeEntity) throws AlipayApiException {

        AlipayClient alipayClient = alipaySdk.getAlipayClient();
        //获取appID
        String appid = alipaySdk.getAlipayConfig().getAppId();

        // 构造请求参数以调用接口

        AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
        //回调接口
        request.setNotifyUrl(notifyUrl);

        AlipayTradeCreateModel model = new AlipayTradeCreateModel();
        // 设置商户订单号
        model.setOutTradeNo(alipayTradeEntity.getOutTradeNo());
        // 设置产品码
        model.setProductCode("JSAPI_PAY");
        // 设置小程序支付中
        model.setOpAppId(appid);
        // 设置订单总金额
        model.setTotalAmount(alipayTradeEntity.getTotalAmount());
        // 设置订单标题
        model.setSubject("充值充电");

        // 设置订单附加信息
        model.setBody(alipayTradeEntity.getBody());


        // 设置买家支付宝用户ID
        model.setBuyerOpenId(alipayTradeEntity.getOpenId());

        request.setBizModel(model);
        AlipayTradeCreateResponse response = alipayClient.execute(request);


        JSONObject jsonObject = JSONUtil.parseObj(response.getBody());

        log.info(response.getBody());
        if (response.isSuccess()) {
            Object o = jsonObject.get("alipay_trade_create_response");
            //把参数信息转换为JSON对象
            JSONObject result = JSONUtil.parseObj(o);

            return new ResponseResult(200, "调用成功", result);
        } else {
            Object o = jsonObject.get("alipay_trade_create_response");
            //把参数信息转换为JSON对象
            JSONObject result = JSONUtil.parseObj(o);
            log.error("支付宝小程序下单失败:" + response.getBody());
            return new ResponseResult(203, (String) result.get("sub_msg"), result);
        }

    }

    @Override
    @Transactional
    public String payNotify(HttpServletRequest request, HttpServletResponse response)  {

        //获取回调参数 并且验签
        Map<String, String> params  = getNotifyBody( request);

        if (params == null) {
            return "fail";
        }

        //强制读主库
        HintManager.getInstance().setMasterRouteOnly();
        try{
            //支付回到验签成功 但是交易状态不是 交易支付成功
            if(!params.get("trade_status").equals("TRADE_SUCCESS")){
                return "success";
            }
            if( params.get("refund_fee") !=null){
                //代表是退款
                return "success";
            }
            //获取我们自己生成的订单号
            String outTradeNo = params.get("out_trade_no");
            //根据订单号查询充值表信息
            Recharge recharge = rechargeService.selectByRechargeOrderNoLock(outTradeNo);

            //支付宝单号
            String tradeNo = params.get("trade_no");
            //交易金额
            String totalAmount = params.get("total_amount");
            //充值信息不存在 直接不管
            if(recharge==null){
                log.error("支付宝原生支付回调，回调充值订单数据不存在，回调参数："+JSONUtil.toJsonStr(params));
                return "success";
            }
            BigDecimal balanceAdd = new BigDecimal(totalAmount);
            if (! (balanceAdd.compareTo(recharge.getRechargeAmount()) == 0)) {
                //金额不一致 不管
                log.error("支付宝原生支付回调，充值表金额与支付回调金额不一致，回调参数："+JSONUtil.toJsonStr(params));
                return "success";
            }
            if (recharge.getPaymentStatus() != 0) {
                //状态不是未支付
                log.error("支付宝原生支付回调，充值表金额状态不是未支付，第三方单号:"+tradeNo);
                return "success";
            }
            //支付渠道
            recharge.setPaymentChannel(2);
            //不需要分账 分账状态直接修改为分账成功
            recharge.setLedgerStatus(1);

            //充值回调的逻辑 有三个支付 写一个公共的方法
            rechargeService.rechargeCallbackProcessLogic(recharge);

        }finally {
            //释放设置
            HintManager.clear();
        }


        return "success";
    }

    /**
     * 支付宝申请退款
     * @return
     */
    @Override
    public ResponseResult refunds() {
        //查询支付渠道是2的退款订单 sql if判断一下如果是3 就用in (3,4) if是2 就直接用 = 2
        List<Recharge> rechargeList = rechargeService.selectByRemainingAmountByPaymentChannel(2);
        if (rechargeList.size() == 0) {

            return new ResponseResult(200, true);
        }
        AlipayClient alipayClient = alipaySdk.getAlipayClient();
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        com.alibaba.fastjson.JSONObject bizContent = new com.alibaba.fastjson.JSONObject();
        //退款回调地址
        request.setNotifyUrl(notifyUrl);
        RefundOrder refundOrder = new RefundOrder();
        for (Recharge recharge : rechargeList) {

            //如果退款金额是0  不需要管
            if (recharge.getRemainingAmount().compareTo(new BigDecimal(0)) == 0) {
                recharge.setRefundStatus(1);
                rechargeService.updateById(recharge);
                continue;
            }
            String replaceUUID = UUID.randomUUID().toString().replace("-", "");

            //支付宝支付订单号
            refundOrder.setTransactionId(recharge.getThirdOrderNumber());
            //商户退款单号
            refundOrder.setOutRefundNo(replaceUUID);
            //电站id
            refundOrder.setStationId(recharge.getStationId());
            refundOrderService.add(refundOrder);

            //原生支付宝退款逻辑方法
            weiLaiElectricAlipayRefund(recharge, replaceUUID, bizContent, alipayClient, request, refundOrder);

        }

        return new ResponseResult(200, false);
    }

    /**
     * 原生支付宝退款逻辑方法
     *
     * @param recharge
     * @param replaceUUID
     * @param bizContent
     * @param alipayClient
     * @param request
     */
    private void weiLaiElectricAlipayRefund(Recharge recharge, String replaceUUID,
                                            com.alibaba.fastjson.JSONObject bizContent,
                                            AlipayClient alipayClient,
                                            AlipayTradeRefundRequest request,
                                            RefundOrder refundOrder)  {


        try{
            bizContent.put("trade_no", recharge.getThirdOrderNumber());
            bizContent.put("refund_amount", recharge.getRemainingAmount().toString());
            bizContent.put("out_request_no", replaceUUID);

            request.setBizContent(bizContent.toString());
            //调用退款接口
            AlipayTradeRefundResponse response = alipayClient.execute(request);

            cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(response.getBody());

            Object o = jsonObject.get("alipay_trade_refund_response");

            //把参数信息转换为JSON对象
            cn.hutool.json.JSONObject result = JSONUtil.parseObj(o);
            log.info("支付宝退款成功返回参数:"+result);
            boolean isFail = false;
            //调用失败
            if (!response.isSuccess()) {
                isFail = true;
                log.error("支付宝退款调用失败:" + response.getBody());
            }
            if (!result.get("fund_change").equals("Y")) {
                isFail = true;
                log.error("支付宝退款调用成功，但是退款状态不是退款成功:" + response.getBody());
            }

            if (isFail) {
                //退款失败
                recharge.setRefundStatus(3);
                rechargeService.updateById(recharge);
                refundOrder.setStatus("失败");
                String reasonFailureReason = "退款失败了";
                if (result.get("sub_msg") != null) {
                    reasonFailureReason = result.get("sub_msg").toString();
                }
                refundOrder.setReasonFailureReason((reasonFailureReason));
                refundOrderService.updateById(refundOrder);
                //退款失败了发送邮件
                //发送邮件
                String to = "709286540@qq.com";
                String title = "汇付退款失败";
                String content = "充值订单号:" + recharge.getThirdOrderNumber();
                emailUtil.sendMessage(to, title, content);
                return;
            }

            //不需要回调了 直接退款成功
            recharge.setRefundStatus(1);
            recharge.setRefundAmount(recharge.getRemainingAmount().add(recharge.getRefundAmount()));
            //修改剩余余额
            recharge.setRemainingAmount(new BigDecimal(0));
            //不需要分账  直接 赋值分账时间
            recharge.setLedgerTime(new Date());
            //分账状态
            recharge.setLedgerStatus(1);
            rechargeService.updateById(recharge);


            refundOrder.setStatus("SUCCESS");
            //用户退款金额
            String refundFee = (String)result.get("refund_fee");

            refundOrder.setPayerRefund(new BigDecimal(refundFee));
            //退款金额
            refundOrder.setRefund(recharge.getRemainingAmount().add(recharge.getRefundAmount()));
            refundOrderService.updateById(refundOrder);

            //退款成功 生成退款记录
            BalanceChange balanceChange = new BalanceChange();
            //用户id
            balanceChange.setMemberId(recharge.getMemberId());
            //类型 收入
            balanceChange.setType(1);
            //备注
            balanceChange.setRemarks("余额退款");
            //金额
            balanceChange.setAmount(recharge.getRefundAmount());
            //变动之前
            balanceChange.setBalanceBefore(new BigDecimal(0));
            //变动之后
            balanceChange.setBalanceAfter(new BigDecimal(0));
            //场景 退款成功 7
            balanceChange.setScene(7);
            //渠道
            balanceChange.setChannel(recharge.getChannel());
            balanceChangeService.add(balanceChange);
        }catch (Exception e){
            log.error("支付宝退款失败:"+e.getMessage());
        }

    }



    //解析回调参数 并且验签
    private Map<String, String> getNotifyBody(HttpServletRequest request)  {

        String alipayPublicKey = alipaySdk.getAlipayConfig().getAlipayPublicKey();

        Map< String , String > params = new HashMap < String , String > ();

        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put (name,valueStr);
        }
        //调用SDK验证签名
        //公钥验签示例代码
        try {
            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2");
            //公钥证书验签示例代码
            //   boolean flag = AlipaySignature.rsaCertCheckV1(params,alipayPublicCertPath,"UTF-8","RSA2");
            if (signVerified) {
                // TODO 验签成功后
                //按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success
                return params;
            } else {
                log.error("支付宝支付回调验签失败:"+params);
                return null;
            }
        }catch (Exception e){
            log.error("支付宝支付回调验签报错:"+params);
            return null;
        }
    }


    private Map<String, String> falseMsg(HttpServletResponse response) {
        Map<String, String> resMap = new HashMap<>(8);
        //失败应答
        response.setStatus(500);
        resMap.put("code", "fail");
        resMap.put("message", "通知验签失败");
        return resMap;
    }

    private Map<String, String> trueMsg(HttpServletResponse response) {
        Map<String, String> resMap = new HashMap<>(8);
        //成功应答
        response.setStatus(200);
        resMap.put("code", "success");
        resMap.put("message", "成功");
        return resMap;
    }



}
